﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Noris.Schedule.Support
{
	/// <summary>
	/// Objekt, který dovoluje volajícímu svěřit mu vykonání akce na konci jeho života.
	/// Jinými slovy: pokud volající chce na konci určitého bloku svých operací provést jistou operaci, a to i při chybě !!!, 
	/// pak typicky implementuje try - finally blok, kde ve finally provede potřebnou akci.
	/// Anebo může využít služeb tohoto objektu, sdělit mu co se má na konci provést, a použít patternu using.
	/// Na konci bloku using bude provedeno dispose tohoto objektu a ten provede požadovanou akci.
	/// Volitelně se může využít i předání akce "na začátku bloku", což je jen bonbónek navíc.
    /// Objekt se má vytvářet statickým konstruktorem: DoOn.End() a DoOn.BeginEnd().
	/// </summary>
	public class DoOn : IDisposable
	{
		/// <summary>
        /// Privátní konstruktor. Používat se má statická varianta: DoOn.End() a DoOn.BeginEnd()
		/// </summary>
		/// <param name="doOnEnd"></param>
		private DoOn(Action doOnEnd)
		{
			this._DoOnEnd = doOnEnd;
			this._IsDone = false;
		}
		/// <summary>
		/// Akce prováděná na konci života
		/// </summary>
		private Action _DoOnEnd;
		private bool _IsDone;
		/// <summary>
		/// Vrátí objekt, který na konci svého života vykoná něco důležitého.
        /// Co to bude, se specifikuje v předaném delegátu.
        /// Poznámka 1: anonymní delegát se píše takto: DoOn.End(delegate() { akce1(); akce2(); proměnná = false; })
        /// Poznámka 2: návratovou hodnotu není třeba v using patternu explicitně ukládat, stačí takto:   using (DoOn.End(akce)) { blok operací; }
		/// </summary>
		/// <param name="doOnEnd">Akce, kterou má objekt vykonat na konci svého života.</param>
		/// <returns></returns>
		public static DoOn End(Action doOnEnd)
		{
			return new DoOn(doOnEnd);
		}
		/// <summary>
        /// Vrátí objekt, který na konci svého života vykoná něco důležitého.
        /// Co to bude, se specifikuje v předaném delegátu doOnEnd.
        /// Navíc vykoná něco důležitého (doOnBegin) i na začátku svého života (tj. teď) - pro usnadnění práce volajícímu.
        /// Poznámka 1: anonymní delegát se píše takto: DoOn.End(delegate() { akce1(); akce2(); proměnná = false; })
        /// Poznámka 2: návratovou hodnotu není třeba v using patternu explicitně ukládat, stačí takto:   using (DoOn.End(akce)) { blok operací; }
        /// </summary>
		/// <param name="doOnBegin">Akce, kterou má objekt vykonat na začátku svého života.</param>
		/// <param name="doOnEnd">Akce, kterou má objekt vykonat na konci svého života.</param>
		/// <returns></returns>
		public static DoOn BeginEnd(Action doOnBegin, Action doOnEnd)
		{
			if (doOnBegin != null)
				doOnBegin();

			return new DoOn(doOnEnd);
		}
		/// <summary>
		/// Na konci života něco uděláme...
		/// </summary>
		void IDisposable.Dispose()
		{
			if (this._DoOnEnd != null && !this._IsDone)
			{
				try
				{
					this._DoOnEnd();
					this._IsDone = true;
				}
				catch
				{ }
			}
		}
	}
}
